/*
 * ComplexFloat.cs
 * 
 * Copyright (c) 2003-2005, dnAnalytics. All rights reserved.
*/

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace dnA.Math {
	///<summary>complex float type.</summary>
	///<remarks>See <see cref="dnA.Math.ComplexMath">CompleMath</see> for complex math functions.</remarks>
	///<seealso cref="dnA.Math.ComplexMath"/>
	[Serializable]
	[StructLayout (LayoutKind.Sequential, CharSet=CharSet.Ansi)]
	public struct ComplexFloat: ICloneable, IFormattable {

		private static readonly ComplexFloat zero = new ComplexFloat(0);
		private static readonly ComplexFloat one = new ComplexFloat(1);

		private float real;
		private float imag;
		
		///<summary>Constructor for complex float precision number type</summary>
		///<param name="real">Real value of complex number expressed as <c>float</c>.</param>
		public ComplexFloat(float real):this(real,0){}
		
		///<summary>Constructor for complex float precision number type</summary>
		///<param name="real">Real value of complex number expressed as <c>float</c>.</param>
		///<param name="imag">Imaginary part of complex number expressed as <c>float</c>.</param>
		public ComplexFloat(float real, float imag) {
			this.real = real;
			this.imag = imag;
		}
		
		///<summary>Created a <c>ComplexFloat</c> from the given string. The string can be in the
		///following formats: <c>n</c>, <c>ni</c>, <c>n +/- ni</c>, <c>n,n</c>, <c>n,ni</c>,
		///<c>(n,n)</c>, or <c>(n,ni)</c>, where n is a real number.</summary>
		///<param name="s">The string to create the <c>ComplexFloat</c> from.</param>
		///<exception cref="FormatException">if the n, is not a number.</exception>
		///<exception cref="ArgumentNullException">if s, is <c>null</c>.</exception>
		public ComplexFloat(string s) {
			this = ComplexFloat.Parse(s);
		}
		
		///<summary>Copy Constructor for complex float number</summary>
		///<param name="complex"><c>ComplexFloat</c> to create new <c>ComplexFloat</c> from.</param>
		public ComplexFloat(ComplexFloat complex):this(complex.real,complex.Imag){}
		
		///<summary>Constant value of one.</summary>
		public static ComplexFloat One {
			get {
				return one;
			}
		}
		
		///<summary>Constant value of zero.</summary>
		public static ComplexFloat Zero {
			get {
				return zero;
			}
		}
		
		///<summary>Property to access Real component</summary>
		public float Real {
			get {
				return this.real;
			}
		}
		
		///<summary>Property to access Imaginary component</summary>
		public float Imag {
			get {
				return this.imag;
			}
		}

		///<summary>Return the Hashcode for the <c>ComplexFloat</c></summary>
		///<returns>The Hashcode representation of <c>ComplexFloat</c></returns>
		override public int GetHashCode(){
			return (int)System.Math.Exp(ComplexMath.Absolute(this));
		}

		///<summary>Check if <c>ComplexFloat</c> variable is the same as another object</summary>
		///<param name="obj"><c>obj</c> to compare present <c>ComplexFloat</c> to.</param>
		///<returns>Returns true if the variable is the same as the <c>ComplexFloat</c> variable</returns>
		///<remarks>The <c>obj</c> parameter is converted into a <c>ComplexFloat</c> variable before comparing with the current <c>ComplexFloat</c>.</remarks>
		public bool Equals(ComplexDouble obj) {
			return this.Real == obj.Real && this.Imag == obj.Imag;
		}
		
		///<summary>Check if <c>ComplexFloat</c> variable is the same as another object</summary>
		///<param name="obj"><c>obj</c> to compare present <c>ComplexFloat</c> to.</param>
		///<returns>Returns true if the variable is the same as the <c>ComplexFloat</c> variable</returns>
		///<remarks>The <c>obj</c> parameter is converted into a <c>ComplexFloat</c> variable before comparing with the current <c>ComplexFloat</c>.</remarks>
		public bool Equals(ComplexFloat obj) {
			return this.Real == obj.Real && this.Imag == obj.Imag;
		}
		
		///<summary>Check if <c>ComplexFloat</c> variable is the same as another object</summary>
		///<param name="obj"><c>obj</c> to compare present <c>ComplexFloat</c> to.</param>
		///<returns>Returns true if the variable is the same as the <c>ComplexFloat</c> variable</returns>
		///<remarks>The <c>obj</c> parameter is converted into a <c>ComplexFloat</c> variable before comparing with the current <c>ComplexFloat</c>.</remarks>
		public override bool Equals(Object obj) {
			if( obj == null ){
				return false;
			}
			if( obj is ComplexFloat ){
				ComplexFloat rhs = (ComplexFloat)obj;
				return this.Equals(rhs);
			} else if(obj is ComplexDouble ){
				ComplexDouble rhs = (ComplexDouble)obj;
				return this.Equals(rhs);
			}else{
				return false;
			}

		}

		///<summary>Equal operator to compare two <c>ComplexFloat</c> variables</summary>
		///<remarks>Returns false if the two variables are not equals using Equals function</remarks>
		public static bool operator ==(ComplexFloat o1, ComplexFloat o2) {
			return o1.Equals(o2);
		}

		///<summary>Not Equal operator to compare two <c>ComplexFloat</c> variables</summary>
		///<remarks>Returns false if the two variables are equal using Equals function</remarks>
		public static bool operator !=(ComplexFloat o1, ComplexFloat o2) {
			return !o1.Equals(o2);
		}

		///<summary>Explicit conversion from <c>ComplexDouble</c> type</summary>
		public static explicit operator ComplexFloat(ComplexDouble value){
			return new ComplexFloat((float)value.Real, (float)value.Imag);
		}

		///<summary>Explicit conversion from <c>ComplexDouble</c> type</summary>
		public static ComplexFloat ToComplexFloat(ComplexDouble value){
			return new ComplexFloat((float)value.Real, (float)value.Imag);
		}
		
		///<summary>Implicit conversion from float type</summary>
		public static implicit operator ComplexFloat(float value){
			return new ComplexFloat(value);
		}

		///<summary>Convert double type to ComplexFloat</summary>
		///<param name="value"><c>float</c> variable as real to</param>
		public static ComplexFloat ToComplex(float value){
			return new ComplexFloat(value);
		}
		
		///<summary>Power operator for x^y</summary>
		///<param name="lhs"><c>ComplexFloat</c> value as base</param>
		///<param name="rhs"><c>ComplexFloat</c> value as exponent</param>
		public static ComplexFloat operator ^(ComplexFloat lhs, ComplexFloat rhs){
			return ComplexMath.Exp( rhs*ComplexMath.Log(lhs) );
		}
		
		///<summary>Raise 'lhs' to the power of 'rhs'</summary>
		///<param name="lhs"><c>ComplexFloat</c> value as base</param>
		///<param name="rhs"><c>ComplexFloat</c> value as exponent</param>
		public static ComplexFloat Pow(ComplexFloat lhs, ComplexFloat rhs){
			return lhs ^ rhs;
		}

		///<summary>Positive Operator</summary>
		public static ComplexFloat operator +( ComplexFloat value ){
			return value;
		}
		///<summary>Positive Operator</summary>
		public static ComplexFloat Plus( ComplexFloat value ){
			return value;
		}
		
		///<summary>Addition Operator</summary>
		public static ComplexFloat operator +( ComplexFloat lhs, ComplexFloat rhs ){
			return new ComplexFloat( lhs.Real + rhs.Real, lhs.Imag + rhs.Imag );
		}
		///<summary>Addition Operator</summary>
		public static ComplexFloat Add( ComplexFloat lhs, ComplexFloat rhs ){
			return lhs + rhs;
		}
		///<summary>Negate Operator</summary>
		public static ComplexFloat operator -( ComplexFloat value ){
			return new ComplexFloat(-value.Real, -value.Imag);
		}
		///<summary>Negate Operator</summary>
		public static ComplexFloat Negate( ComplexFloat value ){
			return -value;
		}

		///<summary>Subtraction Operator</summary>
		public static ComplexFloat operator -( ComplexFloat lhs, ComplexFloat rhs ){
			return new ComplexFloat( lhs.Real - rhs.Real, lhs.Imag - rhs.Imag );
		}
		///<summary>Subtraction Operator</summary>
		public static ComplexFloat Subtract( ComplexFloat lhs, ComplexFloat rhs ){
			return lhs - rhs;
		}

		///<summary>Multiplication Operator</summary>
		public static ComplexFloat operator *( ComplexFloat lhs, ComplexFloat rhs ){
			return new ComplexFloat( lhs.Real*rhs.Real - lhs.Imag*rhs.Imag, lhs.Real*rhs.Imag + lhs.Imag*rhs.Real );
		}
		///<summary>Multiplication Operator</summary>
		public static ComplexFloat Multiply( ComplexFloat lhs, ComplexFloat rhs ){
			return lhs * rhs;
		}

		///<summary>Division Operator</summary>
		public static ComplexFloat operator /( ComplexFloat lhs, ComplexFloat rhs ){
			float denom = rhs.Real*rhs.Real + rhs.Imag*rhs.Imag;
			return new ComplexFloat((lhs.Real*rhs.Real + lhs.Imag*rhs.Imag) / denom, (lhs.Imag*rhs.Real - lhs.Real*rhs.Imag) / denom );

		}
		///<summary>Division Operator</summary>
		public static ComplexFloat Divide( ComplexFloat lhs, ComplexFloat rhs ){
			return lhs / rhs;
		}
		
        ///<summary>Tests whether the the complex number is not a number.</summary>
        ///<returns>True if either the real or imaginary components are NaN, false otherwise.</returns>
		public bool IsNaN(){
			return (Single.IsNaN(real) || Single.IsNaN(imag));
		}

        ///<summary>Tests whether the the complex number is infinite.</summary>
        ///<returns>True if either the real or imaginary components are infinite, false otherwise.</returns>
		public bool IsInfinity(){
			return (Single.IsInfinity(real) || Single.IsInfinity(imag));
		}
		
		///<summary>Make a deep copy of this <c>ComplexFloat</c></summary>
		public ComplexFloat Clone(){
			return new ComplexFloat(this.real, this.imag);
		}
		
		// --- ICloneable Interface ---
		///<summary>Make a deep copy of this <c>ComplexFloat</c></summary>
		Object ICloneable.Clone(){
			return this.Clone();
		}
		
		// --- IFormattable Interface ---
        ///<summary>A string representation of this <c>ComplexFloat</c>.</summary>
        ///<returns>The string representation of the value of <c>this</c> instance.</returns>
		public override string ToString() {
			return ToString(null,null);
		}

        ///<summary>A string representation of this <c>ComplexFloat</c>.</summary>
        ///<param name="format">A format specification.</param>
        ///<returns>The string representation of the value of <c>this</c> instance as specified by format.</returns>
        public string ToString(string format) {
			return ToString(format, null);
		}

        ///<summary>A string representation of this <c>ComplexFloat</c>.</summary>
        ///<param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information.</param>
        ///<returns>The string representation of the value of <c>this</c> instance as specified by provider.</returns>
        public string ToString(IFormatProvider formatProvider) {
			return ToString(null,formatProvider);
		}

        ///<summary>A string representation of this <c>ComplexFloat</c>.</summary>
        ///<param name="format">A format specification.</param>
        ///<param name="formatProvider">An IFormatProvider that supplies culture-specific formatting information.</param>
        ///<returns>The string representation of the value of <c>this</c> instance as specified by format and provider.</returns>
        public string ToString(string format, IFormatProvider formatProvider){
			if( IsNaN() ){ 
				return "NaN";
			}
			if( IsInfinity() ){
				return "IsInfinity";
			}
            StringBuilder ret = new StringBuilder();

            ret.Append(real.ToString(format, formatProvider));
            if( imag < 0 ){
                ret.Append(" ");
            } else{
                ret.Append(" + ");
            }
            ret.Append(imag.ToString(format, formatProvider)).Append("i");

            return ret.ToString();
		}

		/// <summary>Creates a <c>ComplexFloat</c> based on a string. The string can be in the
		///following formats: <c>n</c>, <c>ni</c>, <c>n +/- ni</c>, <c>n,n</c>, <c>n,ni</c>,
		///<c>(n,n)</c>, or <c>(n,ni)</c>, where n is a real number.</summary>
		/// <param name="s">the string to parse.</param>
		/// <returns></returns>
		public static ComplexFloat Parse(String s){
			if( s == null){
				throw new ArgumentNullException(s, "s cannot be null.");
			}
			s = s.Trim();
			if( s.Length == 0 ){
				throw new FormatException();
			}
			
			//check if one character strings are valid
			if( s.Length == 1 ){
				if( String.Compare(s,"i") == 0){
					return new ComplexFloat(0,1);
				}else{
					return new ComplexFloat(Single.Parse(s));
				}
			}
			
			//strip out perens
			if( s.StartsWith("(") ){
				if( !s.EndsWith(")") ){
					throw new FormatException();
				}
				else{
					s = s.Substring(1,s.Length-2);
				}
			}
			
			string real = s;
			string imag = "0";
			
			//comma seperated
			int index = s.IndexOf(',');
			if(index > -1 ){
				real = s.Substring(0, index);
				imag = s.Substring(index + 1, s.Length - index - 1);
			}else{
				index = s.IndexOf('+',1);
				if( index > -1 ){
					real = s.Substring(0, index);
					imag = s.Substring(index + 1, s.Length - index - 1);
				}else{
					index = s.IndexOf('-',1);
					if( index > -1 ){
						real = s.Substring(0, index);
						imag = s.Substring(index, s.Length - index);
					}
				}
			}
			
			//see if we have numbers in the format xxxi
			if( real.EndsWith("i") ) {
				if( !imag.Equals("0") ){
					throw new FormatException();
				} else{
					imag = real.Substring(0,real.Length-1);
					real = "0";
				}
			}	
			if( imag.EndsWith("i")){
				imag = imag.Substring(0,imag.Length - 1);
			}
			//handle cases of - n, + n
			if( real.StartsWith("-")){
				real = "-" + real.Substring(1,real.Length-1).Trim();
			}
			if( imag.StartsWith("-")){
				imag = "-" + imag.Substring(1,imag.Length-1).Trim();
			}
			
			ComplexFloat ret;
			try{
				ret = new ComplexFloat(Single.Parse(real.Trim()), Single.Parse(imag.Trim()));
			}catch(Exception){
				throw new FormatException();
			}
			return ret;
		}
	}
}
